package edu.ccut.saturn.studio.saturndata.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import edu.ccut.saturn.component.IDataItem;
import edu.ccut.saturn.component.IValidateInfo;
import edu.ccut.saturn.component.IValidateUseInfo;
import edu.ccut.saturn.component.impl.SaturnValidateUseInfo;
import edu.ccut.saturn.studio.core.util.xml.SaturnW3CUtil;

public class ValidateModel {

	public static final String VALIDATES_ATTR_IS_VALIDATE = "isValidate";
	public static final String VALIDATES_ATTR_VALUE_KEY = "value-key";
	public static final String VALIDATES_ATTR_CLASS_TYPE = "classType";
	public static final String FIELD_ELEMENT_VALIDATES = "validates";
	public static final String VALIDATES_ELEMENT_VALIDATE = "validate";
	public static final String EVENT_INFOS_ELEMENT_EVENT_INFO = "eventInfo";
	public static final String ERROR_INFOS_ELEMENT_ERROR_INFO = "errorInfo";
	public static final String ERROR_INFO_ATTR_KEY = "key";
	public static final String VALIDATE_ELEMENT_VALIDATE_TYPE = "validateType";
	public static final String VALIDATE_ELEMENT_EVENT_INFOS = "eventInfos";
	public static final String VALIDATE_ELEMENT_ERROR_INFOS = "errorInfos";
	public static final String VALIDATE_ELEMENT_CUSTOM_JS_METHOD = "custom-js-method";
	public static final String VALIDATE_ELEMENT_JS_METHOD_NAME = "js-method-name";
	public static final String VALIDATE_ELEMENT_KEY = "key";
	public static final String VALIDATE_ELEMENT_ARGS = "args";
	public static final String ARGS_ELEMENT_ARG = "arg";
	public static final String VALIDATE_ELEMENT_CLASS = "class";
	public static final String VALIDATE_ELEMENT_NAME = "name";
	public static final String VALIDATE_ELEMENT_DESCRIPTION = "description";
	
	private Document document;
	
	private ValidateVO validateVO = null;

	private static ValidateModel instance = new ValidateModel();

	private ValidateModel() {

	}
	
	public ValidateModel(Document document) {
		this.document = document;

	}

	public static ValidateModel getInstance() {
		return instance;
	}

	public SaturnValidateUseInfo loadSaturnValidateUseInfo(Node validatesNode) {
		if (validatesNode != null) {
			boolean isValidateBoolean = false;

			String isValidate = SaturnW3CUtil.getNodeAttributeValue(
					validatesNode, VALIDATES_ATTR_IS_VALIDATE);

			if ("true".equalsIgnoreCase(isValidate)) {
				isValidateBoolean = true;
			}

			String classType = SaturnW3CUtil.getNodeAttributeValue(
					validatesNode, VALIDATES_ATTR_CLASS_TYPE);
			String valueKey = SaturnW3CUtil.getNodeAttributeValue(validatesNode,
					VALIDATES_ATTR_VALUE_KEY);

			List<ValidateVO> validateList = this.loadValidates(validatesNode);

			List<IValidateInfo> validateInfos = null;
			if (validateList != null) {
				validateInfos = new ArrayList<IValidateInfo>();

				for (ValidateVO validateVO : validateList) {
					validateInfos.add((IValidateInfo) validateVO);
				}
			}

			SaturnValidateUseInfo saturnValidateUseInfo = new SaturnValidateUseInfo(
					valueKey, classType, isValidateBoolean, validateInfos);

			return saturnValidateUseInfo;
		}

		return null;
	}
	
	public void updateErrorinfo(Map<String, String> errorinfoList) {
		Node validateNode = this.getValidateNode();
		Node errorInfosNode = SaturnW3CUtil.getFirstChildByName(validateNode,
				VALIDATE_ELEMENT_ERROR_INFOS);
		SaturnW3CUtil.removeChildren(errorInfosNode);
	
		for (String key : this.validateVO.getErrorInfos().keySet()) {
			Node errorinfoNode = SaturnW3CUtil.createChildNode(errorInfosNode,
					ERROR_INFOS_ELEMENT_ERROR_INFO, this.validateVO
							.getErrorInfos().get(key));
			SaturnW3CUtil.createAttribute(errorinfoNode,
					VALIDATE_ELEMENT_KEY, key);
		}
	
		SaturnW3CUtil.format(validateNode);
	
		if (this.validateVO != null) {
			this.validateVO.setErrorInfo(errorinfoList);
		}
	}
	
	private Node getValidateNode() {
		Node validateNode = SaturnW3CUtil.getFirstChildByName(this.document
				.getDocumentElement(), VALIDATES_ELEMENT_VALIDATE);
		return validateNode;
	}
	
	public void createValidateUseInfo(Node fieldNode, IDataItem dataItem) {
		SaturnW3CUtil.removeChildren(fieldNode);

		IValidateUseInfo validateUseInfo = dataItem.getValidateUseInfo();

		if (validateUseInfo != null) {
			Node validatesNode = SaturnW3CUtil.createChildNode(fieldNode,
					FIELD_ELEMENT_VALIDATES, null);
			SaturnW3CUtil.createAttribute(validatesNode,
					VALIDATES_ATTR_VALUE_KEY, validateUseInfo.getValueKey());
			SaturnW3CUtil.createAttribute(validatesNode,
					VALIDATES_ATTR_CLASS_TYPE, validateUseInfo
							.getValueClassType());
			SaturnW3CUtil.createAttribute(validatesNode,
					VALIDATES_ATTR_IS_VALIDATE, validateUseInfo.isValidate()
							+ ""); //$NON-NLS-1$

			List<IValidateInfo> validateInfos = validateUseInfo
					.getValidateInfos();
			if (validateInfos != null) {
				for (IValidateInfo validateInfo : validateInfos) {
					this.createValidateInfoNode(validatesNode, validateInfo);
				}
			}
		}
	}
	
	public void updateValidateChildValue(String childName, String newValue) {
		Node validateNode = this.getValidateNode();
		SaturnW3CUtil.setChildNodeValue(validateNode, childName, newValue);

		SaturnW3CUtil.format(this.document.getDocumentElement());

		if (this.validateVO != null) {
			if (childName.equals(VALIDATE_ELEMENT_CLASS)) {
				this.validateVO.setClassTypeStr(newValue);
			}
			if (childName.equals(VALIDATE_ELEMENT_DESCRIPTION)) {
				this.validateVO.setDescription(newValue);
			}
			if (childName.equals(VALIDATE_ELEMENT_CUSTOM_JS_METHOD)) {
				this.validateVO.setCustemJSMethodInfo(newValue);
			}
			if (childName.equals(VALIDATE_ELEMENT_JS_METHOD_NAME)) {
				this.validateVO.getJSMethodName();
			}
		}
	}
	
	public void createValidateInfoNode(Node validatesNode,
			IValidateInfo validateInfo) {

		Node validateNode = SaturnW3CUtil.createChildNode(validatesNode,
				VALIDATES_ELEMENT_VALIDATE, null);

		SaturnW3CUtil.createChildNode(validateNode, VALIDATE_ELEMENT_KEY,
				validateInfo.getKey());
		SaturnW3CUtil.createChildNode(validateNode, VALIDATE_ELEMENT_NAME,
				validateInfo.getName());
		SaturnW3CUtil.createChildNode(validateNode, VALIDATE_ELEMENT_CLASS,
				validateInfo.getClassTypeStr());
		SaturnW3CUtil.createChildNode(validateNode,
				VALIDATE_ELEMENT_DESCRIPTION, validateInfo.getDescription());
		SaturnW3CUtil
				.createChildNode(validateNode, VALIDATE_ELEMENT_JS_METHOD_NAME,
						validateInfo.getJSMethodName());
		SaturnW3CUtil.createChildNode(validateNode,
				VALIDATE_ELEMENT_CUSTOM_JS_METHOD, validateInfo
						.getCustemJSMethodInfo());
		SaturnW3CUtil.createChildNode(validateNode,
				VALIDATE_ELEMENT_VALIDATE_TYPE, validateInfo.getValidateType());

		this.createValidateInfoErrors(validateNode, validateInfo);
		this.createValidateInfoEvents(validateNode, validateInfo);
		this.createValidateInfoArgs(validateNode, validateInfo);
	}

	public List<ValidateVO> loadValidates(Node validatesNode) {
		List<ValidateVO> validateList = new ArrayList<ValidateVO>();

		List<Node> validateNodes = SaturnW3CUtil.getChildrenByName(
				validatesNode, VALIDATES_ELEMENT_VALIDATE);

		for (Node validateNode : validateNodes) {
			String name = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_NAME);
			String clazz = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_CLASS);
			String key = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_KEY);
			String description = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_DESCRIPTION);
			String jsMethodName = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_JS_METHOD_NAME);
			String custemJSMethodInfo = SaturnW3CUtil.getChildNodeValue(
					validateNode, VALIDATE_ELEMENT_CUSTOM_JS_METHOD);
			String validateType = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_VALIDATE_TYPE);

			Map<String, String> errors = loadValidateErrors(validateNode);
			List<String> events = loadValidateEvents(validateNode);
			List<String> args = loadValidateArgs(validateNode);

			ValidateVO validateInfo = new ValidateVO(key, name, description,
					errors, args, clazz, events, validateType);

			validateInfo.setJSMethodName(jsMethodName);
			validateInfo.setCustemJSMethodInfo(custemJSMethodInfo);

			validateList.add(validateInfo);
		}

		return validateList;
	}

	private List<String> loadValidateArgs(Node validateNode) {
		List<String> args = new ArrayList<String>();

		Node argsNode = SaturnW3CUtil.getFirstChildByName(validateNode,
				VALIDATE_ELEMENT_ARGS);

		if (argsNode != null) {
			List<Node> argNodes = SaturnW3CUtil.getChildrenByName(argsNode,
					ARGS_ELEMENT_ARG);

			if (argNodes != null) {
				for (Node arg : argNodes) {
					args.add(SaturnW3CUtil.getNodeValue(arg));
				}
			}
		}

		return args;
	}

	private List<String> loadValidateEvents(Node validateNode) {
		List<String> events = new ArrayList<String>();

		Node eventsNode = SaturnW3CUtil.getFirstChildByName(validateNode,
				VALIDATE_ELEMENT_EVENT_INFOS);

		if (eventsNode != null) {
			List<Node> eventNodes = SaturnW3CUtil.getChildrenByName(eventsNode,
					EVENT_INFOS_ELEMENT_EVENT_INFO);

			if (eventNodes != null) {
				for (Node event : eventNodes) {
					events.add(SaturnW3CUtil.getNodeValue(event));
				}
			}
		}

		return events;
	}

	private Map<String, String> loadValidateErrors(Node validateNode) {
		Map<String, String> errors = new HashMap<String, String>();
		Node errorsNode = SaturnW3CUtil.getFirstChildByName(validateNode,
				VALIDATE_ELEMENT_ERROR_INFOS);

		if (errorsNode != null) {
			List<Node> errorNodes = SaturnW3CUtil.getChildrenByName(errorsNode,
					ERROR_INFOS_ELEMENT_ERROR_INFO);

			if (errorNodes != null) {
				for (Node error : errorNodes) {
					String errorKey = SaturnW3CUtil.getNodeAttributeValue(error,
							ERROR_INFO_ATTR_KEY);
					errors.put(errorKey, SaturnW3CUtil.getNodeValue(error));
				}
			}
		}

		return errors;
	}
	
	
	public ValidateVO getValidateVO() {
		
		if (validateVO == null) {
			Node validateNode = this.getValidateNode();
	
			String key = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_KEY);
			String description = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_DESCRIPTION);
			String clazz = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_CLASS);
			String jsMethod = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_JS_METHOD_NAME);
			String customJsMethod = SaturnW3CUtil.getChildNodeValue(
					validateNode, VALIDATE_ELEMENT_CUSTOM_JS_METHOD);
			String name = SaturnW3CUtil.getChildNodeValue(validateNode,
					VALIDATE_ELEMENT_NAME);
	
			List<String> args = this.getArgs();
			Map<String, String> errors = this.getErrors();
	
			this.validateVO = new ValidateVO(key, name, description, errors,
					args, clazz, null, null);
			this.validateVO.setJSMethodName(jsMethod);
			this.validateVO.setCustemJSMethodInfo(customJsMethod);
		}
	
		return this.validateVO;
	}
	
	public List<String> getArgs() {
		Node validateNode = this.getValidateNode();
		Node argsNode = SaturnW3CUtil.getFirstChildByName(validateNode,
				VALIDATE_ELEMENT_ARGS);
		List<Node> argNodes = SaturnW3CUtil.getChildrenByName(argsNode,
				ARGS_ELEMENT_ARG);

		List<String> args = new ArrayList<String>();

		for (Node argNode : argNodes) {
			args.add(SaturnW3CUtil.getNodeValue(argNode));
		}

		return args;
	}

	/**
	 * 获得ErrValidate的
	 * 
	 * @return
	 */
	private Map<String, String> getErrors() {
		Map<String, String> errors = new HashMap<String, String>();

		Node validateNode = this.getValidateNode();
		Node ErrorInfosNodes = SaturnW3CUtil.getFirstChildByName(validateNode,
				VALIDATE_ELEMENT_ERROR_INFOS);
		List<Node> errorNodes = SaturnW3CUtil.getChildrenNode(ErrorInfosNodes);

		for (Node errorNode : errorNodes) {
			String errorInfo = SaturnW3CUtil.getNodeAttributeValue(errorNode,
					VALIDATE_ELEMENT_KEY);
			String errorInfoValue = SaturnW3CUtil.getNodeValue(errorNode);
			errors.put(errorInfo, errorInfoValue);
		}

		return errors;
	}
	
	/**
	 * 更新arg节点的值
	 * 
	 * @param args
	 */
	public void updateArg(List<String> args) {
		Node validateNode = this.getValidateNode();
		Node argsNode = SaturnW3CUtil.getFirstChildByName(validateNode,
				VALIDATE_ELEMENT_ARGS);
		SaturnW3CUtil.removeChildren(argsNode);

		for (String arg : args) {
			SaturnW3CUtil.createChildNode(argsNode, ARGS_ELEMENT_ARG, arg);
		}

		SaturnW3CUtil.format(argsNode);

		if (this.validateVO != null) {
			this.validateVO.setArgs(args);
		}
	}

	private void createValidateInfoErrors(Node validateNode,
			IValidateInfo validateInfo) {
		Map<String, String> errorInfos = validateInfo.getErrorInfos();
		Set<String> keySet = errorInfos.keySet();

		if (keySet != null) {
			Node errorInfosNode = SaturnW3CUtil.createChildNode(validateNode,
					VALIDATE_ELEMENT_ERROR_INFOS, null);

			for (String key : keySet) {
				Node errorInfoNode = SaturnW3CUtil.createChildNode(
						errorInfosNode, ERROR_INFOS_ELEMENT_ERROR_INFO,
						errorInfos.get(key));

				SaturnW3CUtil.createAttribute(errorInfoNode,
						VALIDATE_ELEMENT_KEY, key);
			}
		}
	}

	private void createValidateInfoEvents(Node validateNode,
			IValidateInfo validateInfo) {
		List<String> eventInfos = validateInfo.getEventInfos();

		if (eventInfos != null) {
			Node eventInfosNode = SaturnW3CUtil.createChildNode(validateNode,
					VALIDATE_ELEMENT_EVENT_INFOS, null);

			for (String eventInfo : eventInfos) {
				SaturnW3CUtil.createChildNode(eventInfosNode,
						EVENT_INFOS_ELEMENT_EVENT_INFO, eventInfo);
			}
		}
	}

	private void createValidateInfoArgs(Node validateNode,
			IValidateInfo validateInfo) {
		List<String> args = validateInfo.getArgs();
		if (args != null) {
			Node argsNode = SaturnW3CUtil.createChildNode(validateNode,
					VALIDATE_ELEMENT_ARGS, null);

			validateNode.appendChild(argsNode);
			for (String arg : args) {
				SaturnW3CUtil.createChildNode(argsNode, ARGS_ELEMENT_ARG, arg);
			}
		}
	}
}
